#ifndef XRPL_OVERLAY_PREDICATES_H_INCLUDED
#define XRPL_OVERLAY_PREDICATES_H_INCLUDED

#include <xrpld/overlay/Message.h>
#include <xrpld/overlay/Peer.h>

#include <set>

namespace ripple {

/** Sends a message to all peers */
struct send_always
{
    using return_type = void;

    std::shared_ptr<Message> const& msg;

    send_always(std::shared_ptr<Message> const& m) : msg(m)
    {
    }

    void
    operator()(std::shared_ptr<Peer> const& peer) const
    {
        peer->send(msg);
    }
};

//------------------------------------------------------------------------------

/** Sends a message to match peers */
template <typename Predicate>
struct send_if_pred
{
    using return_type = void;

    std::shared_ptr<Message> const& msg;
    Predicate const& predicate;

    send_if_pred(std::shared_ptr<Message> const& m, Predicate const& p)
        : msg(m), predicate(p)
    {
    }

    void
    operator()(std::shared_ptr<Peer> const& peer) const
    {
        if (predicate(peer))
            peer->send(msg);
    }
};

/** Helper function to aid in type deduction */
template <typename Predicate>
send_if_pred<Predicate>
send_if(std::shared_ptr<Message> const& m, Predicate const& f)
{
    return send_if_pred<Predicate>(m, f);
}

//------------------------------------------------------------------------------

/** Sends a message to non-matching peers */
template <typename Predicate>
struct send_if_not_pred
{
    using return_type = void;

    std::shared_ptr<Message> const& msg;
    Predicate const& predicate;

    send_if_not_pred(std::shared_ptr<Message> const& m, Predicate const& p)
        : msg(m), predicate(p)
    {
    }

    void
    operator()(std::shared_ptr<Peer> const& peer) const
    {
        if (!predicate(peer))
            peer->send(msg);
    }
};

/** Helper function to aid in type deduction */
template <typename Predicate>
send_if_not_pred<Predicate>
send_if_not(std::shared_ptr<Message> const& m, Predicate const& f)
{
    return send_if_not_pred<Predicate>(m, f);
}

//------------------------------------------------------------------------------

/** Select the specific peer */
struct match_peer
{
    Peer const* matchPeer;

    match_peer(Peer const* match = nullptr) : matchPeer(match)
    {
    }

    bool
    operator()(std::shared_ptr<Peer> const& peer) const
    {
        if (matchPeer && (peer.get() == matchPeer))
            return true;

        return false;
    }
};

//------------------------------------------------------------------------------

/** Select all peers (except optional excluded) that are in our cluster */
struct peer_in_cluster
{
    match_peer skipPeer;

    peer_in_cluster(Peer const* skip = nullptr) : skipPeer(skip)
    {
    }

    bool
    operator()(std::shared_ptr<Peer> const& peer) const
    {
        if (skipPeer(peer))
            return false;

        if (!peer->cluster())
            return false;

        return true;
    }
};

//------------------------------------------------------------------------------

/** Select all peers that are in the specified set */
struct peer_in_set
{
    std::set<Peer::id_t> const& peerSet;

    peer_in_set(std::set<Peer::id_t> const& peers) : peerSet(peers)
    {
    }

    bool
    operator()(std::shared_ptr<Peer> const& peer) const
    {
        if (peerSet.count(peer->id()) == 0)
            return false;

        return true;
    }
};

}  // namespace ripple

#endif
